package com.middlegroup.subjectpay.service.helipay;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;


public class MessageAuthenticationCode {
    public static byte[] mac(byte[] key, byte[] data) throws
            NoSuchAlgorithmException, NoSuchPaddingException,
            NoSuchAlgorithmException, InvalidKeyException, BadPaddingException,
            IllegalBlockSizeException, IllegalStateException {

        return mac(key, data, 0, data.length);
    }

    public static byte[] mac(byte[] key, byte[] data, int offset, int len) throws
            NoSuchAlgorithmException, NoSuchPaddingException,
            NoSuchAlgorithmException, InvalidKeyException, BadPaddingException,
            IllegalBlockSizeException, IllegalStateException {
        final String Algorithm = "DES";

        SecretKey deskey = new SecretKeySpec(key, Algorithm);

        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.ENCRYPT_MODE, deskey);

        byte buf[] = {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        for (int i = 0; i < len; ) {
            for (int j = 0; j < 8 && i < len; i++, j++) {
                buf[j] ^= data[offset + i];
            }
            buf = c1.update(buf);
        }
        c1.doFinal();
        return buf;
    }

    public static byte[] desEncryption(byte[] key, byte[] data) throws
            NoSuchAlgorithmException, Exception {
        final String Algorithm = "DES/ECB/NoPadding";

        if (key.length != DESKeySpec.DES_KEY_LEN || data.length != 8)
            throw new IllegalArgumentException("key or data's length != 8");

        DESKeySpec desKS = new DESKeySpec(key);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
        SecretKey deskey = skf.generateSecret(desKS);

        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.ENCRYPT_MODE, deskey);

        byte buf[];
        buf = c1.doFinal(data);

        byte[] enc_data = new byte[8];
        System.arraycopy(buf, 0, enc_data, 0, 8);
        return enc_data;
    }


    public static byte[] desDecryption(byte[] key, byte[] data) throws
            NoSuchAlgorithmException, Exception {
        final String Algorithm = "DES/ECB/NoPadding";

        if (key.length != DESKeySpec.DES_KEY_LEN || data.length != 8)
            throw new IllegalArgumentException("key's len != 8 or data's length != 8");


        SecretKey deskey = new SecretKeySpec(key, "DES");


        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.DECRYPT_MODE, deskey);

        byte decrypted[];


        decrypted = c1.doFinal(data);

        return decrypted;
    }

    protected byte[] encryptByDES(byte[] bytP, byte[] bytKey) throws Exception {
        DESKeySpec desKS = new DESKeySpec(bytKey);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
        SecretKey sk = skf.generateSecret(desKS);
        Cipher cip = Cipher.getInstance("DES");
        cip.init(Cipher.ENCRYPT_MODE, sk);
        return cip.doFinal(bytP);
    }

    protected byte[] decryptByDES(byte[] bytE, byte[] bytKey) throws Exception {
        DESKeySpec desKS = new DESKeySpec(bytKey);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
        SecretKey sk = skf.generateSecret(desKS);
        Cipher cip = Cipher.getInstance("DES");
        cip.init(Cipher.DECRYPT_MODE, sk);
        return cip.doFinal(bytE);
    }

    public static byte[] des3Encryption(byte[] key, byte[] data) throws
            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, IllegalStateException {
        final String Algorithm = "DESede";

        SecretKey deskey = new SecretKeySpec(key, Algorithm);

        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.ENCRYPT_MODE, deskey);
        return c1.doFinal(data);
    }


    public static byte[] des3Decryption(byte[] key, byte[] data) throws
            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, IllegalStateException {
        final String Algorithm = "DESede";

        SecretKey deskey = new SecretKeySpec(key, Algorithm);

        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.DECRYPT_MODE, deskey);
        return c1.doFinal(data);
    }

    public static byte[] des3Encryption(byte[] key, byte[] iv, byte[] data) throws
            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, IllegalStateException, InvalidAlgorithmParameterException, InvalidKeySpecException {
        final String Algorithm = "DESede/CBC/PKCS5Padding";
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(
                "DESede");
        DESedeKeySpec spec = new DESedeKeySpec(key);
        SecretKey deskey = keyFactory.generateSecret(spec);
        IvParameterSpec tempIv = new IvParameterSpec(iv);
        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.ENCRYPT_MODE, deskey, tempIv);
        return c1.doFinal(data);

    }

    public static byte[] des3Decryption(byte[] key, byte[] iv, byte[] data) throws
            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, IllegalStateException, InvalidAlgorithmParameterException, InvalidKeySpecException {
        final String Algorithm = "DESede/CBC/PKCS5Padding";
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
        DESedeKeySpec spec = new DESedeKeySpec(key);
        SecretKey deskey = keyFactory.generateSecret(spec);
        IvParameterSpec tempIv = new IvParameterSpec(iv);
        Cipher c1 = Cipher.getInstance(Algorithm);
        c1.init(Cipher.DECRYPT_MODE, deskey, tempIv);
        return c1.doFinal(data);

    }

}